
/*
 * Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved. Use is
 * subject to license terms.
 */ 

package org.jdesktop.application;

import java.awt.event.KeyEvent;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import javax.swing.AbstractButton;
import javax.swing.JLabel;


/**
 * An internal helper class that configures the text and mnemonic 
 * properties for instances of AbstractButton, JLabel, and 
 * javax.swing.Action.  It's used like this:
 * <pre>
 * MnemonicText.configure(myButton, "Save &As")
 * </pre>
 * The configure method unconditionally sets three properties on the 
 * target object: 
 * <ul>
 * <li>the label text, "Save As" 
 * <li>the mnemonic key code, VK_A
 * <li>the index of the mnemonic character, 5
 * </ul>
 * If the mnemonic marker character isn't present, then the second
 * two properties are cleared to VK_UNDEFINED (0) and -1 respectively.
 * <p>
 */
class MnemonicText {
    private MnemonicText() { } // not used

    public static void configure(Object target, String markedText) {
        String text = markedText;
        int mnemonicIndex = -1;
        int mnemonicKey = KeyEvent.VK_UNDEFINED;
        // TBD: mnemonic marker char should be an application resource
        int markerIndex = mnemonicMarkerIndex(markedText, '&');
        if (markerIndex == -1) {
            markerIndex = mnemonicMarkerIndex(markedText, '_');
        }
        if (markerIndex != -1) {
            text = text.substring(0, markerIndex) + text.substring(markerIndex + 1);
            mnemonicIndex = markerIndex;
            CharacterIterator sci = new StringCharacterIterator(markedText, markerIndex);
            mnemonicKey = mnemonicKey(sci.next());
        }
        if (target instanceof javax.swing.Action) {
            configureAction((javax.swing.Action)target, text, mnemonicKey, mnemonicIndex);
        }
        else if (target instanceof AbstractButton) {
            configureButton((AbstractButton)target, text, mnemonicKey, mnemonicIndex);
        }
        else if (target instanceof JLabel) {
            configureLabel((JLabel)target, text, mnemonicKey, mnemonicIndex);
        }
        else {
            throw new IllegalArgumentException("unrecognized target type " + target);
        }
    }

    private static int mnemonicMarkerIndex(String s, char marker) {
        if ((s == null) || (s.length() < 2)) { return -1; }
        CharacterIterator sci = new StringCharacterIterator(s);  
        int i = 0;
        while(i != -1) {
            i = s.indexOf(marker, i);
            if (i != -1) {
                sci.setIndex(i);
                char c1 = sci.previous();
                sci.setIndex(i);
                char c2 = sci.next();
                boolean isQuote = (c1 == '\'') && (c2 == '\'');
                boolean isSpace = Character.isWhitespace(c2);
                if (!isQuote && !isSpace && (c2 != CharacterIterator.DONE)) {
                    return i;
                }
            }
            if (i != -1) { i += 1; }
        }
        return -1;
    }

    /* A general purpose way to map from a char to a KeyCode is needed.  An 
     * AWT RFE has been filed: 
     * http://bt2ws.central.sun.com/CrPrint?id=6559449
     * CR 6559449 java/classes_awt Support for converting from char to KeyEvent VK_ keycode
     */
    private static int mnemonicKey(char c) {
        int vk = (int)c;
        if ((vk >= 'a') && (vk <='z')) {
            vk -= ('a' - 'A');
        }
        return vk;
    }

    /* This javax.swing.Action constants is only 
     * defined in Mustang (1.6), see 
     * http://download.java.net/jdk6/docs/api/javax/swing/Action.html
     */
    private static final String DISPLAYED_MNEMONIC_INDEX_KEY = "SwingDisplayedMnemonicIndexKey";

    private static void configureAction(javax.swing.Action target, String text, int key, int index) {
        target.putValue(javax.swing.Action.NAME, text);
        if (key != KeyEvent.VK_UNDEFINED) { target.putValue(javax.swing.Action.MNEMONIC_KEY, key); }
        if (index != -1) { target.putValue(DISPLAYED_MNEMONIC_INDEX_KEY, index); }
    }

    private static void configureButton(AbstractButton target, String text, int key, int index) {
        target.setText(text);
        if (key != KeyEvent.VK_UNDEFINED) { target.setMnemonic(key); }
        if (index != -1) { target.setDisplayedMnemonicIndex(index); }
    }

    private static void configureLabel(JLabel target, String text, int key, int index) {
        target.setText(text);
        if (key != KeyEvent.VK_UNDEFINED) { target.setDisplayedMnemonic(key); }
        if (index != -1) { target.setDisplayedMnemonicIndex(index); }
    }
}
